/*
 * @Description:
 * @Author: maxyang
 * @Date: 2022-05-17 10:54:58
 * @LastEditTime: 2022-08-09 22:20:56
 * @LastEditors: liutq
 * @Reference:
 */
/*
 * @Description:
 * @Author: maxyang
 * @Date: 2022-05-17 10:54:58
 * @LastEditTime: 2022-06-07 12:13:10
 * @LastEditors: liutq
 * @Reference:
 */
package service

import (
	"crypto/md5"
	cryrand "crypto/rand"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"math/big"
	"math/rand"
	"strconv"
	"time"

	"github.com/maxyang107/jinglan/aliyun"
	"github.com/maxyang107/jinglan/common"
	"github.com/maxyang107/jinglan/model"
	"github.com/maxyang107/jinglan/model/goods"
	"github.com/maxyang107/jinglan/model/sms"
	"github.com/maxyang107/jinglan/model/system"
	"github.com/maxyang107/jinglan/model/user"
	"github.com/maxyang107/jinglan/utils"
	"github.com/spf13/cast"
)

var (
	// 所有的业务类都在这儿声明
	UserService = &UserSer{}
)

type UserServices interface {
	// 根据手机号查询用户信息
	FindUserByPhone(phone int64) *user.User
	CheckLogin(*user.User) (string, error)
	Register(*common.RegisterStruct) error
}

type UserSer struct {
}

func (u UserSer) FindUserByPhone(phone int64) *user.User {
	return user.FindUserByPhone(phone)
}

/**
 * @description: 方法描述：登录验证
 * @Author: maxyang
 * @return {*}
 * @param {*user.User} userP
 */
func (u UserSer) CheckLogin(userP *user.User) (string, error) {
	userInfo := u.FindUserByPhone(userP.Phone)

	if userInfo.ID == 0 {
		return "", errors.New("用户不存在")
	}
	if userInfo.Password != u.Md5(userP.Password, userInfo.Salt) {
		return "", errors.New("用户密码不正确")
	}

	if userInfo.Status != 1 {
		return "", errors.New("当前用户已被禁用！")
	}

	return common.GenToken(userInfo.ID)
}

func (u UserSer) CheckAdminLogin(userP *user.User) (string, error) {
	userInfo := u.FindUserByPhone(userP.Phone)

	if userInfo.Role != 2 {
		return "", errors.New("非管理员角色无法登录")
	}

	if userInfo.ID == 0 {
		return "", errors.New("用户不存在")
	}
	if userInfo.Password != u.Md5(userP.Password, userInfo.Salt) {
		return "", errors.New("用户密码不正确")
	}

	return common.AdminGenToken(userInfo.ID)
}

func (u UserSer) Md5(str string, salt string) string {
	str = str + salt
	h := md5.New()
	h.Write([]byte(str))
	return hex.EncodeToString(h.Sum(nil))
}

/**
 * @description: 方法描述：注册
 * @Author: maxyang
 * @return {*}
 * @param {common.RegisterStruct} register_
 */
func (u *UserSer) Register(register_ common.RegisterStruct) error {

	headimgs := []string{
		"https://jingnandata.oss-cn-chengdu.aliyuncs.com/headimg/10%402x.png",
		"https://jingnandata.oss-cn-chengdu.aliyuncs.com/headimg/11%402x.png",
		"https://jingnandata.oss-cn-chengdu.aliyuncs.com/headimg/12%402x.png",
		"https://jingnandata.oss-cn-chengdu.aliyuncs.com/headimg/13%402x.png",
		"https://jingnandata.oss-cn-chengdu.aliyuncs.com/headimg/14%402x.png",
		"https://jingnandata.oss-cn-chengdu.aliyuncs.com/headimg/15%402x.png",
		"https://jingnandata.oss-cn-chengdu.aliyuncs.com/headimg/16%402x.png",
		"https://jingnandata.oss-cn-chengdu.aliyuncs.com/headimg/9%402x.png",
	}

	err := u.optionLimit(register_.Ip, "reg")
	if err != nil {
		return errors.New("注册次数超出上限")
	}

	verCode := common.Verfycode{}
	verCode.Code = register_.Code
	verCode.Phone = register_.Phone
	verCode.Scene = 1

	//验证手机验证码
	if err := u.Verfycode(verCode); err != nil {
		return err
	}

	//验证身份证号码格式
	// if !utils.IsIDCard(register_.Idnum) {
	// 	return errors.New("身份证号码不正确")
	// }

	//验证手机号码格式
	if !utils.CheckMobile(strconv.Itoa(int(register_.Phone))) {
		return errors.New("手机号格式错误")
	}

	//手机三要素验证
	// ps, err2 := aliyun.CheckPhoneNameIdCard(register_.Phone, register_.Username, register_.Idnum)
	// if err2 != nil || ps.Code != 200 {
	// 	return errors.New("实名认证失败")
	// }

	tmpuser := user.FindUserByPhoneOrIdnum(register_.Phone, register_.Idnum)
	if tmpuser.ID > 0 {
		return errors.New("手机号或身份证已被占用")
	}

	var user1 user.User
	rand.Seed(time.Now().Unix())
	user1.Idnum = register_.Idnum
	user1.InviteCode = utils.CreatInviteCode(4)
	user1.Salt = strconv.Itoa(rand.Intn(100000) + 1000)
	user1.Password = u.Md5(register_.Password, user1.Salt)
	user1.Username = register_.Username
	user1.Phone = register_.Phone
	rand.Seed(time.Now().UnixNano())
	user1.HeadImg = headimgs[rand.Intn(len(headimgs))]
	//获取邀请者
	if register_.FromCode != "" {
		tmpuser := user.FindUserByInviteCode(register_.FromCode)
		user1.Fromuid = tmpuser.ID

	}
	user1.Status = 1

	return user.Register(&user1)
}

/**
 * @description: 方法描述：操作限制
 * @Author: maxyang
 * @return {*}
 * @param {string} clientIp
 * @param {string} scene
 */
func (u *UserSer) optionLimit(clientIp string, scene string) error {
	key := u.Md5(clientIp, scene)
	ipnum := model.GetKey(key)
	iplimit, _ := strconv.Atoi(ipnum)
	if iplimit > 4 {
		return errors.New("调用次数超出限制")
	}

	iplimit++
	//一天只允许调用4次
	model.SetKey(key, strconv.Itoa(iplimit), 3600*24*time.Second)
	return nil
}

/**
 * @description: 方法描述：发送验证码
 * @Author: maxyang
 * @return {*}
 * @param {common.SmsCode} smscode_
 */
func (u *UserSer) SendSmsCode(smscode_ common.SmsCode) error {
	// err := u.optionLimit(smscode_.Ip, fmt.Sprintf("%s:%d", "send_code", smscode_.Scene))
	// if err != nil {
	// 	return errors.New("发送次数达到上限")
	// }

	record := sms.FindSmsCodeByPhoneAndSeace(smscode_.Phone, uint8(smscode_.Scene))

	if record.Expiretime != nil && record.Expiretime.Unix() > time.Now().Unix() {
		return errors.New("发送验证码过快")
	}
	rand.Seed(time.Now().Unix())
	sms_ := sms.VerificationCode{}
	sms_.Phone = smscode_.Phone
	sms_.Scene = smscode_.Scene
	sms_.SmsCode = strconv.Itoa(rand.Intn(1000) + 1000)

	seedtime, _ := time.ParseDuration("10m")
	now := time.Now()
	expire_ := now.Add(seedtime)
	sms_.Expiretime = &expire_

	sms.Save(sms_)
	aliyun.SendMsg(smscode_.Phone, sms_.SmsCode)
	return nil
}

/**
 * @description: 方法描述：根据场景验证验证码
 * @Author: maxyang
 * @return {*}
 * @param {common.Verfycode} code_
 */
func (u *UserSer) Verfycode(code_ common.Verfycode) error {
	vc := sms.FindSmsCodeByPhoneAndSeace(code_.Phone, uint8(code_.Scene))

	if vc.Expiretime == nil {
		return errors.New("验证码不存在")
	}

	if vc.Expiretime.Unix() < time.Now().Unix() {
		return errors.New("验证码已过期")
	}

	if vc.SmsCode != code_.Code {
		return errors.New("验证码不匹配")
	}

	sms.Update(vc)
	return nil
}

/**
 * @description: 方法描述：获取邀请列表
 * @Author: maxyang
 * @return {*}
 * @param {common.AnnouncementList} query_
 */
func (u *UserSer) GetInviteList(query_ common.AnnouncementList) *user.UserInviteList {
	if query_.PerPage == 0 {
		query_.PerPage = 10
	}

	uil := user.GetInviteList(query_)

	for k, inv := range uil.Data {
		uil.Data[k].Phone = utils.FormatMobileStar(inv.Phone)
	}
	return uil
}

func (u *UserSer) GetUserCollectList(query_ common.PramCollect) *user.CollectList {
	if query_.PerPage == 0 {
		query_.PerPage = 10
	}

	return user.GetUserCollectList(query_)
}

//初始化盲盒奖品
func (u *UserSer) initGiftPool(blindBoxId uint) (*[]common.Gift, goods.GoodsTmp) {
	var gifts []common.Gift
	gt := goods.GetGoodsDetail(int(blindBoxId))
	s := model.GetKey(gt.GoodsBlindCode)
	if s != "" {
		json.Unmarshal([]byte(s), &gifts)
		return &gifts, gt
	}
	sc := system.GetSysConfigDetail(gt.GoodsBlindCode)
	if sc.ConfigValue != "" {
		model.SetKey(gt.GoodsBlindCode, sc.ConfigValue, 0)
		json.Unmarshal([]byte(sc.ConfigValue), &gifts)
	}
	return &gifts, gt
}

func (u *UserSer) OpenBlindBox(param_ common.Detail) (*common.OpenGift, error) {

	//检查盲盒是否是当前用户拥有
	mc := user.GetUserCollectById(param_.UserId, param_.Id)
	if mc.ID == 0 {
		return nil, errors.New("非法访问")
	}

	if mc.IsOpen == 1 {
		return nil, errors.New("盲盒已经开过啦")
	}
	//获取盲盒详细信息
	gt2 := goods.GetGoodsDetail(int(mc.GoodsId))
	if gt2.GoodsOptionId != 0 {
		mc2 := user.GetUserCollectByGoodsId(param_.UserId, gt2.GoodsOptionId)
		gt3 := goods.GetGoodsDetail(int(gt2.GoodsOptionId))
		if mc2.ID == 0 {
			return nil, errors.New("缺乏开启盲盒必要的藏品：" + gt3.GoodsName)
		}
		//销毁藏品
		mc2.DeleteTag = 1

		err3 := user.UpdateMyCollect(mc2)
		if err3 != nil {
			return nil, errors.New("盲盒开启失败！详情请联系客服！")
		}
	}

	//将盲盒状态修改成已开启
	mc.IsOpen = 1
	user.UpdateMyCollect(mc)

	//初始化当前盲盒奖池
	giftsPool, goodsTmp := u.initGiftPool(mc.GoodsId)

	// 获取用户信息
	userinfo := user.FindUserByUserId(param_.UserId)
	//执行开奖
	blindBoxResult, err := u.doOpenBlindBox(*giftsPool, goodsTmp.GoodsBlindCode, userinfo)
	if err != nil {
		return nil, err
	}

	//将中奖结果存到数据库中
	err2 := u.saveMyCollect(blindBoxResult.Id, param_.UserId, 5, goodsTmp.GoodsCirculation)
	if err2 != nil {
		return nil, err
	}

	//获取商品信息
	gt := goods.GetGoodsDetail(int(blindBoxResult.Id))
	var opengift common.OpenGift

	opengift.Id = blindBoxResult.Id
	opengift.Name = blindBoxResult.Name
	opengift.Type = blindBoxResult.Type
	opengift.ListImg = gt.GoodsListImg
	//返回给用户中奖结果
	return &opengift, nil
}

//执行开奖
func (u *UserSer) doOpenBlindBox(arr []common.Gift, key string, userinfo *user.User) (common.Gift, error) {
	var leng = 0 //默认都是100即为100%
	for i := 0; i < len(arr); i++ {
		leng += arr[i].Prop
	}
	for i := 0; i < len(arr); i++ {
		result, err := cryrand.Int(cryrand.Reader, big.NewInt(cast.ToInt64(leng)))
		if err != nil {
			return common.Gift{
				Name:  "很遗憾，本次什么也没有开出！下次继续努力吧！",
				Prop:  0,
				Type:  5,
				Count: 0,
				Id:    0,
			}, errors.New("未中奖")
		}
		random_str := result.String()
		random := cast.ToInt(random_str)
		if random < (arr[i].Prop + int(userinfo.LuckyNum)/10) {
			if arr[i].Count > 0 {
				tmp := arr[i]
				arr[i].Count = arr[i].Count - 1
				//将中奖结果保存到redis中
				b, _ := json.Marshal(arr)
				model.SetKey(key, string(b), 0)
				updateLuckyNum(userinfo, tmp.LuckyNum)
				return tmp, nil
			} else {
				return common.Gift{
					Name:  "很遗憾，本次什么也没有开出！下次继续努力吧！",
					Prop:  0,
					Type:  5,
					Count: 0,
					Id:    0,
				}, errors.New("未中奖")
			}
		} else {
			leng -= arr[i].Prop
		}
	}
	return common.Gift{
		Name:  "很遗憾，本次什么也没有开出！下次继续努力吧！",
		Prop:  0,
		Type:  5,
		Count: 0,
		Id:    0,
	}, errors.New("未中奖")
}

func updateLuckyNum(userInfo *user.User, rediceNum uint) {
	if userInfo.LuckyNum == 0 {
		return
	}
	if userInfo.LuckyNum > rediceNum {
		userInfo.LuckyNum = userInfo.LuckyNum - rediceNum
	} else {
		userInfo.LuckyNum = 0
	}
	user.ModifyLuckyNumber(userInfo)
}

/**
 * @description: 方法描述：保存藏品
 * @Author: maxyang
 * @return {*}
 * @param {uint} goodsId_
 * @param {uint} userId_
 * @param {uint8} collecttype_
 * @param {uint} goodsCirculation
 */
func (u *UserSer) saveMyCollect(goodsId_ uint, userId_ uint, collecttype_ uint8, goodsCirculation uint) error {
	//获取商品信息
	gt := goods.GetGoodsDetail(int(goodsId_))

	if gt.ID == 0 {
		return errors.New("没有找到该商品")
	}

	return user.Buildcollect(&gt, userId_, collecttype_, "", 1, 1)
}

func (u *UserSer) GetUserCollectDetail(query_ common.Detail) *user.MyCollectDetail {
	return user.GetUserCollectDetailById(query_.UserId, query_.Id)
}

func (u *UserSer) GetInviteRankList(query_ common.AnnouncementList) *user.RankInviteList {
	if query_.PerPage == 0 {
		query_.PerPage = 10
	}

	ril := user.GetInviteRankList(query_)
	for k, v := range ril.Data {
		ril.Data[k].Phone = utils.FormatMobileStar(v.Phone)
	}
	return ril
}

func (u *UserSer) ModifyPassword(param common.ModifyPassword) error {
	user1 := user.FindUserByPhone(param.Phone)

	if user1.ID == 0 {
		return errors.New("没有找到该用户")
	}

	//验证验证码
	verCode := common.Verfycode{}
	verCode.Code = param.Code
	verCode.Phone = param.Phone
	verCode.Scene = 2

	//验证手机验证码
	if err := u.Verfycode(verCode); err != nil {
		return err
	}

	user1.Password = u.Md5(param.Password, user1.Salt)
	fmt.Println(user1.Password)

	return user.ModifyPassword(user1)
}

func (u *UserSer) GetUserInfo(userId uint) *user.User {
	return user.FindUserByUserId(userId)
}
